package com.bghx.bghxmvc.core;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.bghx.bghxmvc.annotation.Autowired;
import com.bghx.bghxmvc.annotation.Controller;
import com.bghx.bghxmvc.annotation.RequestMapping;
import com.bghx.bghxmvc.annotation.RequestParam;
import com.bghx.bghxmvc.annotation.Service;



@SuppressWarnings("serial")
public class DispatcherServlet extends HttpServlet {
	
	private List<String> classNames = new ArrayList<>();//指定路径下的类文件名，由scanPackages扫描配置文件中的路径后生成
    private Map<String, Object> instanceMap = new HashMap<>();//保存实例化后的Service和Controller
    
    private Map<String, Object> functionMap = new HashMap<>();//保存处理请求的方法列表	

    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        //设置页面编码
    	resp.setContentType("text/html;charset=utf-8");
        resp.setCharacterEncoding("UTF-8");
        
        String path = req.getServletPath();
        String ctValue = path.split("/")[1];
        
        
        
        Object object = instanceMap.get(ctValue);//取出参数对应的控制器
        
        if (object!=null) {
        	
        	
        	
            Method method = (Method) functionMap.get(path);//取出对应的方法
            
            if (method!=null) {


            	
                Parameter[] parameters = method.getParameters();//取出方法的所有参数
                //生成参数列表 开始
                Object[] args = new Object[parameters.length];
                
                for (int i = 0; i < parameters.length; i++) {
                	
                    if (parameters[i].isAnnotationPresent(RequestParam.class)) {//是否存在着方法参数
                    	
                        RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
                        
                        String rpValue = requestParam.value();//参数注解的设置值
                        
                        String value = req.getParameter(rpValue);//HttpServletRequest里，取得用户提交相应的参数
                        
                        Class clazz = parameters[i].getType();//获取参数类型
                        
                        if ("int".equals(clazz.getName())) {//如果是int类型
                            args[i] = Integer.valueOf(value);//转换为Integer类实例
                        } else if ("java.lang.String".equals(clazz.getName())) {//如果是String类型
                            args[i] = value;//字符串直接存到参数列表
                        }
                    }
                }                
            	
                try {
                    Object obj = null;//用来保存调用返回值
                    //调用方法
                    if (parameters.length > 0) {
                        obj = method.invoke(object, args);
                    } else {
                        obj = method.invoke(object, null);
                    }
                    resp.getWriter().print(obj.toString());//将返回对象转成String输出到页面
                    
                    
                } catch (Exception e) {
                    resp.getWriter().print(e.toString());//把异常信息输出来页面
                }            	
            	
            	
			}else {
				resp.getWriter().print("找不到对应的方法");
			}
            

            
		}else {
			
			resp.getWriter().print("找不到对应的控制器");
		}
        
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        
    	doGet(req, resp);//直接调用get的处理方法
        
    }    
    
    /*
     * 初始化
     * 在 Servlet 的生命期中，仅执行一次 init() 方法。它是在服务器装入 Servlet时执行的。
     */
    @Override
    public void init(ServletConfig config) {
    	
    	//扫描指定路径下的包
    	ServletContext servletContext=config.getServletContext();
    	String scanPath=(String) servletContext.getAttribute("scan-path");
    	scanClasses(scanPath);
    	//加载Service和Controller
    	try {
			loadServiceController();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	
    	gatherFunction();
    	
    	ioc();
    	
    	System.out.println(classNames);
    }
    
    @Override
    public void destroy() {
    	System.out.println("DispatcherServlet结束运行");
    }
    
    

	/*
	* 加载Service和Controller
	* 
	*/
    private void loadServiceController() throws Exception {

        //遍历packageNames变量，即包名
        for (String className : classNames) {
        	
        	//利用反射获得类的字节码
            Class<?> clazz = Class.forName(className);
            
            Object instance = null;
            
            if (clazz.isAnnotationPresent(Controller.class)) {//判断是否存在Controller注解
            
            	//生成一个Controller实例
            	instance = clazz.newInstance();
                Controller controller = (Controller) clazz.getAnnotation(Controller.class);//获得Controller注解
                
                String ctValue = controller.value();//获取注解参数值
                instanceMap.put(ctValue, instance);//保存到instanceMap变量中
                
            } else if (clazz.isAnnotationPresent(Service.class)) {//判断是否存在Service注解
                
            	//生成一个Service实例
            	instance = clazz.newInstance();
            	
                Service service = (Service) clazz.getAnnotation(Service.class);//获得Service注解
                String srValue = service.value();//获取注解参数值
                if ((srValue==null)||(srValue.equals(""))) {
                	srValue=clazz.getName().substring(clazz.getName().lastIndexOf(".")+1);
				}
                instanceMap.put(srValue, instance);//保存到instanceMap变量中
                
            }
        }
	}
    
    /*
     * 从instanceMap找出所有Controller，并将它们的有RequestMapping的方法的引用存到handlerMap变量中
     */
    private void gatherFunction() {

        
        //遍历instanceMap(存放着Controller和Service)
        for (Map.Entry<String, Object> entry : instanceMap.entrySet()) {
        	
            Class<? extends Object> clazz = entry.getValue().getClass();//用反射获得类的字节码
            
            if (clazz.isAnnotationPresent(Controller.class)) {//判断是否有Controller标记
            	
                Controller controller = (Controller) clazz.getAnnotation(Controller.class);//获得Controller注解
                
                String ctValue = controller.value();//获得注解参数值
                
                Method[] methods = clazz.getMethods();//获得类的所有方法
                
                //遍历方法
                for (Method method : methods) {
                	
                    if (method.isAnnotationPresent(RequestMapping.class)) {//判断是否有RequestMapping注解，如果有，才能把相应的请求转发给它
                    	
                        RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                        String reValue = requestMapping.value();
                        functionMap.put("/" + ctValue + "/" + reValue, method);//将引用存入handlerMap中
                        
                    }
                }
            }
        }
    }    
    
    //控制反转，处理Autowired注解
    private void ioc() {
       
        //遍历instanceMap
        for (Map.Entry<String, Object> entry : instanceMap.entrySet()) {
        	
            Field[] fields = entry.getValue().getClass().getDeclaredFields();//利用反射获取字段
            
            for (Field field : fields) {
            	
                if (field.isAnnotationPresent((Class<? extends Annotation>) Autowired.class)) {
                    
                	field.setAccessible(true);//使私有字段可读写
                    Autowired autowired = field.getAnnotation(Autowired.class);//获取field上的Autowired注解
                    
                    String awValue = autowired.value();
                    try {
                    	
                    	//从instanceMap取出相应的Service实例，注入到私有的field中
                        field.set(entry.getValue(), instanceMap.get(awValue));
                        
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                    field.setAccessible(false);
                    
                }
            }
        }
    }    
    
    
    private  void scanClasses(String scanPath) {
        URL url = this.getClass().getClassLoader().getResource("/" + replaceTo(scanPath));//生成扫描路径，包名里的点转成斜杠
        
        String fileName = url.getFile();//转换路径
        
        File file = new File(fileName);//读取文件
        String[] classFileNamelist = file.list();//文件名列表
        
        for (String classFile : classFileNamelist) {//遍历
            File aFile = new File(fileName + classFile);//生成完整文件路径
            if (aFile.isDirectory()) {//如果还是目录
                scanClasses(scanPath + "." + aFile.getName());//递归扫描下级文件夹
            } else {
            	//扫描到的类，存在类私有变量packageNames里
            	classNames.add(scanPath + "." + aFile.getName().substring(0, aFile.getName().indexOf(".")));
            }
        }		
	}
    

    //把.转换为斜杠
    private String replaceTo(String packageName) {
        return packageName.replaceAll("\\.", "/");
    }    
	
}
